package org.cybzacg.convert.converters;

import org.cybzacg.convert.ConversionEngine;
import org.cybzacg.convert.annotation.Converter;
import org.cybzacg.convert.context.ConversionContext;
import org.cybzacg.convert.core.AbstractTypeConverter;
import org.cybzacg.convert.enums.ConversionError;
import org.cybzacg.convert.enums.ConversionPriority;
import org.cybzacg.convert.enums.ConversionType;
import org.cybzacg.convert.exception.ConversionException;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 实体类转换器
 * 实现用户创建的实体类之间的转换
 * 支持字段映射、类型转换、嵌套对象转换等功能
 *
 * <p>主要特性：
 * <ul>
 *   <li>支持同名字段的自动映射</li>
 *   <li>支持不同类型字段的自动转换</li>
 *   <li>支持嵌套对象的递归转换</li>
 *   <li>支持集合和数组字段的转换</li>
 *   <li>支持自定义字段映射规则</li>
 *   <li>支持null值处理和默认值设置</li>
 * </ul>
 *
 * <p>使用示例：
 * <pre>{@code
 * // 基础用法
 * UserDTO userDTO = conversionEngine.convert(userEntity, UserDTO.class);
 *
 * // 带默认值
 * UserDTO userDTO = conversionEngine.convert(userEntity, UserDTO.class, new UserDTO());
 *
 * // 自定义字段映射
 * EntityConverter converter = new EntityConverter();
 * converter.addFieldMapping("userName", "name");
 * converter.addFieldMapping("userAge", "age");
 * UserDTO userDTO = converter.convert(userEntity, UserDTO.class);
 * }</pre>
 *
 * @author cybzacg.blog
 * @version 3.0
 * @since 3.0
 */
@Slf4j
@Converter(
        name = "EntityConverter",
        priority = ConversionPriority.LOW,
        description = "实体类转换器，实现用户创建的实体类之间的转换",
        enabled = true,
        version = "3.0",
        author = "cybzacg.blog",
        tags = {"entity", "object", "mapping", "conversion"}
)
public class EntityConverter extends AbstractTypeConverter {

    /**
     * 字段映射缓存
     * 源类 -> 目标类 -> 字段映射关系
     */
    private static final Map<String, Map<String, String>> FIELD_MAPPING_CACHE = new ConcurrentHashMap<>();

    /**
     * 字段信息缓存
     * 类名 -> 字段信息列表
     */
    private static final Map<String, List<FieldInfo>> FIELD_INFO_CACHE = new ConcurrentHashMap<>();

    /**
     * 自定义字段映射
     * 源字段名 -> 目标字段名
     */
    private final Map<String, String> customFieldMappings = new HashMap<>();

    /**
     * 是否忽略未知字段
     */
    private boolean ignoreUnknownFields = true;

    /**
     * 是否忽略null值字段
     */
    private boolean ignoreNullFields = false;

    /**
     * 是否启用深度复制
     */
    private boolean deepCopy = true;

    /**
     * 构造函数
     */
    public EntityConverter() {
        super(ConversionPriority.LOW);
    }

    @Override
    protected <T> T doConvert(Object source, Class<T> targetType, T defaultValue, ConversionContext context)
            throws Exception {

        if (source == null) {
            return defaultValue;
        }

        Class<?> sourceType = source.getClass();

        // 检查是否为目标类型的实例
        if (targetType.isInstance(source)) {
            @SuppressWarnings("unchecked")
            T result = (T) source;
            return result;
        }

        // 获取目标实例
        T target = createTargetInstance(targetType, defaultValue);
        if (target == null) {
            throw new ConversionException(ConversionError.INSTANTIATION_ERROR,
                    "无法创建目标类型实例: " + targetType.getName());
        }

        // 执行字段映射和转换
        mapFields(source, target, sourceType, targetType, context);

        return target;
    }

    /**
     * 创建目标实例
     *
     * @param <T>          目标类型
     * @param targetType   目标类型
     * @param defaultValue 默认值
     * @return 目标实例
     */
    private <T> T createTargetInstance(Class<T> targetType, T defaultValue) {
        if (defaultValue != null) {
            return defaultValue;
        }

        try {
            // 尝试使用无参构造函数
            return targetType.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            log.warn("无法使用无参构造函数创建实例: {}", targetType.getName(), e);

            // 尝试其他方式创建实例
            try {
                // 尝试使用sun.misc.Unsafe（不推荐，但作为备选方案）
                return createInstanceUnsafe(targetType);
            } catch (Exception ex) {
                log.error("创建目标实例失败: {}", targetType.getName(), ex);
                return null;
            }
        }
    }

    /**
     * 使用Unsafe创建实例（备选方案）
     */
    @SuppressWarnings("unchecked")
    private <T> T createInstanceUnsafe(Class<T> targetType) throws Exception {
        try {
            // 这里可以集成更安全的实例创建方式
            // 暂时返回null，让调用方处理
            return null;
        } catch (Exception e) {
            throw new Exception("无法创建实例: " + targetType.getName(), e);
        }
    }

    /**
     * 执行字段映射和转换
     *
     * @param source     源对象
     * @param target     目标对象
     * @param sourceType 源类型
     * @param targetType 目标类型
     * @param context    转换上下文
     */
    private void mapFields(Object source, Object target, Class<?> sourceType, Class<?> targetType,
                           ConversionContext context) throws Exception {

        // 获取源对象字段信息
        List<FieldInfo> sourceFields = getFieldInfos(sourceType);
        // 获取目标对象字段信息
        List<FieldInfo> targetFields = getFieldInfos(targetType);

        // 创建目标字段映射
        Map<String, FieldInfo> targetFieldMap = new HashMap<>();
        for (FieldInfo fieldInfo : targetFields) {
            targetFieldMap.put(fieldInfo.getName(), fieldInfo);
        }

        // 获取字段映射关系
        Map<String, String> fieldMappings = getFieldMappings(sourceType, targetType);

        // 遍历源字段进行映射
        for (FieldInfo sourceField : sourceFields) {
            try {
                // 获取源字段值
                Object sourceValue = getFieldValue(source, sourceField);

                // 检查是否忽略null值
                if (ignoreNullFields && sourceValue == null) {
                    continue;
                }

                // 确定目标字段名
                String targetFieldName = getTargetFieldName(sourceField.getName(), fieldMappings);
                FieldInfo targetField = targetFieldMap.get(targetFieldName);

                if (targetField == null) {
                    if (!ignoreUnknownFields) {
                        log.warn("目标类型中未找到字段: {}", targetFieldName);
                    }
                    continue;
                }

                // 转换字段值
                Object convertedValue = convertFieldValue(sourceValue, targetField.getType(), context);

                // 设置目标字段值
                setFieldValue(target, targetField, convertedValue);

            } catch (Exception e) {
                log.warn("字段映射失败: {} -> {}", sourceField.getName(),
                        getTargetFieldName(sourceField.getName(), fieldMappings), e);

                if (context.getStrategy().isStrict()) {
                    throw new ConversionException(ConversionError.FIELD_ACCESS_ERROR,
                            "字段映射失败: " + sourceField.getName(), e);
                }
            }
        }
    }

    /**
     * 获取字段信息列表
     *
     * @param clazz 类
     * @return 字段信息列表
     */
    private List<FieldInfo> getFieldInfos(Class<?> clazz) {
        String cacheKey = clazz.getName();
        List<FieldInfo> fieldInfos = FIELD_INFO_CACHE.get(cacheKey);

        if (fieldInfos == null) {
            fieldInfos = new ArrayList<>();
            Class<?> currentClass = clazz;

            // 遍历类继承层次
            while (currentClass != null && currentClass != Object.class) {
                Field[] fields = currentClass.getDeclaredFields();
                for (Field field : fields) {
                    // 跳过静态字段和final字段
                    int modifiers = field.getModifiers();
                    if (Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) {
                        continue;
                    }

                    fieldInfos.add(new FieldInfo(field));
                }
                currentClass = currentClass.getSuperclass();
            }

            FIELD_INFO_CACHE.put(cacheKey, fieldInfos);
        }

        return fieldInfos;
    }

    /**
     * 获取字段映射关系
     *
     * @param sourceType 源类型
     * @param targetType 目标类型
     * @return 字段映射关系
     */
    private Map<String, String> getFieldMappings(Class<?> sourceType, Class<?> targetType) {
        String cacheKey = sourceType.getName() + "->" + targetType.getName();
        Map<String, String> mappings = FIELD_MAPPING_CACHE.get(cacheKey);

        if (mappings == null) {

            // 添加自定义映射
            mappings = new HashMap<>(customFieldMappings);

            // 添加基于名称的自动映射
            List<FieldInfo> sourceFields = getFieldInfos(sourceType);
            List<FieldInfo> targetFields = getFieldInfos(targetType);

            for (FieldInfo sourceField : sourceFields) {
                String sourceFieldName = sourceField.getName();

                // 查找匹配的目标字段
                for (FieldInfo targetField : targetFields) {
                    String targetFieldName = targetField.getName();

                    // 精确匹配
                    if (sourceFieldName.equals(targetFieldName)) {
                        mappings.put(sourceFieldName, targetFieldName);
                        break;
                    }

                    // 驼峰转下划线匹配
                    if (sourceFieldName.equals(toCamelCase(targetFieldName)) ||
                            targetFieldName.equals(toCamelCase(sourceFieldName))) {
                        mappings.put(sourceFieldName, targetFieldName);
                        break;
                    }
                }
            }

            FIELD_MAPPING_CACHE.put(cacheKey, mappings);
        }

        return mappings;
    }

    /**
     * 获取目标字段名
     *
     * @param sourceFieldName 源字段名
     * @param fieldMappings   字段映射
     * @return 目标字段名
     */
    private String getTargetFieldName(String sourceFieldName, Map<String, String> fieldMappings) {
        String targetFieldName = fieldMappings.get(sourceFieldName);
        return targetFieldName != null ? targetFieldName : sourceFieldName;
    }

    /**
     * 获取字段值
     *
     * @param object    对象
     * @param fieldInfo 字段信息
     * @return 字段值
     */
    private Object getFieldValue(Object object, FieldInfo fieldInfo) throws Exception {
        Field field = fieldInfo.getField();
        field.setAccessible(true);
        return field.get(object);
    }

    /**
     * 设置字段值
     *
     * @param object    对象
     * @param fieldInfo 字段信息
     * @param value     字段值
     */
    private void setFieldValue(Object object, FieldInfo fieldInfo, Object value) throws Exception {
        Field field = fieldInfo.getField();
        field.setAccessible(true);
        field.set(object, value);
    }

    /**
     * 转换字段值
     *
     * @param sourceValue 源值
     * @param targetType  目标类型
     * @param context     转换上下文
     * @return 转换后的值
     */
    private Object convertFieldValue(Object sourceValue, Class<?> targetType, ConversionContext context) {
        if (sourceValue == null) {
            return null;
        }

        // 如果类型匹配，直接返回
        if (targetType.isInstance(sourceValue)) {
            return sourceValue;
        }

        try {
            // 使用转换引擎进行类型转换
            if (deepCopy && isComplexType(sourceValue.getClass())) {
                // 对于复杂类型，进行深度转换
                return ConversionEngine.getInstance().convert(sourceValue, targetType, null, context);
            } else {
                // 对于简单类型，直接转换
                return ConversionEngine.getInstance().convert(sourceValue, targetType);
            }
        } catch (Exception e) {
            log.warn("字段值转换失败: {} -> {}", sourceValue.getClass().getName(), targetType.getName(), e);

            if (context.getStrategy().isStrict()) {
                throw new ConversionException(ConversionError.TYPE_MISMATCH,
                        "字段值转换失败: " + sourceValue.getClass().getName() + " -> " + targetType.getName(), e);
            }

            return null;
        }
    }

    /**
     * 检查是否为复杂类型
     *
     * @param type 类型
     * @return 是否为复杂类型
     */
    private boolean isComplexType(Class<?> type) {
        return !type.isPrimitive() &&
                !type.equals(String.class) &&
                !Number.class.isAssignableFrom(type) &&
                !type.equals(Boolean.class) &&
                !type.equals(Character.class) &&
                !type.isEnum() &&
                !type.getName().startsWith("java.time");
    }

    /**
     * 驼峰命名转下划线命名
     *
     * @param camelCase 驼峰命名
     * @return 下划线命名
     */
    private String toSnakeCase(String camelCase) {
        return camelCase.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
    }

    /**
     * 下划线命名转驼峰命名
     *
     * @param snakeCase 下划线命名
     * @return 驼峰命名
     */
    private String toCamelCase(String snakeCase) {
        StringBuilder result = new StringBuilder();
        boolean upperNext = false;

        for (char c : snakeCase.toCharArray()) {
            if (c == '_') {
                upperNext = true;
            } else {
                if (upperNext) {
                    result.append(Character.toUpperCase(c));
                    upperNext = false;
                } else {
                    result.append(Character.toLowerCase(c));
                }
            }
        }

        return result.toString();
    }

    @Override
    public Class<?> getSupportedType() {
        return Object.class;
    }

    @Override
    public boolean supports(Class<?> targetType) {
        // 支持所有非基本类型的对象转换
        return !targetType.isPrimitive() &&
                !targetType.equals(String.class) &&
                !Number.class.isAssignableFrom(targetType) &&
                !targetType.equals(Boolean.class) &&
                !targetType.equals(Character.class) &&
                !targetType.isEnum();
    }

    @Override
    protected ConversionType getConversionType() {
        return ConversionType.OBJECT;
    }

    /**
     * 添加字段映射
     *
     * @param sourceFieldName 源字段名
     * @param targetFieldName 目标字段名
     */
    public void addFieldMapping(String sourceFieldName, String targetFieldName) {
        customFieldMappings.put(sourceFieldName, targetFieldName);
        // 清除缓存以强制重新计算映射
        FIELD_MAPPING_CACHE.clear();
    }

    /**
     * 移除字段映射
     *
     * @param sourceFieldName 源字段名
     */
    public void removeFieldMapping(String sourceFieldName) {
        customFieldMappings.remove(sourceFieldName);
        // 清除缓存以强制重新计算映射
        FIELD_MAPPING_CACHE.clear();
    }

    /**
     * 清除所有字段映射
     */
    public void clearFieldMappings() {
        customFieldMappings.clear();
        // 清除缓存以强制重新计算映射
        FIELD_MAPPING_CACHE.clear();
    }

    /**
     * 设置是否忽略未知字段
     *
     * @param ignoreUnknownFields 是否忽略未知字段
     */
    public void setIgnoreUnknownFields(boolean ignoreUnknownFields) {
        this.ignoreUnknownFields = ignoreUnknownFields;
    }

    /**
     * 设置是否忽略null值字段
     *
     * @param ignoreNullFields 是否忽略null值字段
     */
    public void setIgnoreNullFields(boolean ignoreNullFields) {
        this.ignoreNullFields = ignoreNullFields;
    }

    /**
     * 设置是否启用深度复制
     *
     * @param deepCopy 是否启用深度复制
     */
    public void setDeepCopy(boolean deepCopy) {
        this.deepCopy = deepCopy;
    }

    /**
     * 清除缓存
     */
    public static void clearCache() {
        FIELD_MAPPING_CACHE.clear();
        FIELD_INFO_CACHE.clear();
    }

    /**
     * 字段信息内部类
     */
    private static class FieldInfo {
        private final Field field;
        private final String name;
        private final Class<?> type;

        public FieldInfo(Field field) {
            this.field = field;
            this.name = field.getName();
            this.type = field.getType();
        }

        public Field getField() {
            return field;
        }

        public String getName() {
            return name;
        }

        public Class<?> getType() {
            return type;
        }
    }
}
